#ifndef PACKEDMESSAGE_H
#define PACKEDMESSAGE_H

/*!
 * \file GridPackedMessage.h
 * \author thomas raynal
 * \version 2.0
 */

#include <boost/asio.hpp>
#include <stdio.h>
#include <vector>
#include <iostream>

/*! \namespace Network
 * 
 * Group client/server related classes. Offer API for handling both server and clients.
 */
namespace Network
{
typedef std::vector<boost::uint8_t> data_buffer;
const unsigned TAG_REQUEST = 0;
const unsigned TAG_RESPONSE = 1;
const unsigned HEADER_SIZE = 5;

/*! \class GridPackedMessage
* \brief Wrapper aroung a specified message type. Basic message infos (size, type).
* Message model must be specified
*/
template <class MessageType>
class GridPackedMessage
{
public:
    typedef boost::shared_ptr<MessageType> MessagePointer;

	int messageType_;

    GridPackedMessage(MessagePointer msg = MessagePointer())
        : m_msg(msg)
    {
	}

	/*!
    *  \brief Set the message type (request 0, response 1)
    *
    */
	void set_type (int type)
	{
		messageType_ =type;
	}

	/*!
    *  \brief Reference a message
    *
    */
    void set_msg(MessagePointer msg)
    {
        m_msg = msg;
    }

	/*!
    *  \brief Get a shared ptr on the message
    *
    */
    MessagePointer get_msg()
    {
        return m_msg;
    }

	/*!
    *  \brief Wrap the message
    *
    */
    bool pack(data_buffer& buf) const
    {
        if (!m_msg)
            return false;

        unsigned msg_size = m_msg->ByteSize();
        buf.resize(HEADER_SIZE + msg_size);
        encode_header(buf, msg_size);
		return m_msg->SerializeToArray(&buf[HEADER_SIZE], msg_size);
    }

	/*!
    *  \brief Retrieve the size of the message.
    *
    */
    static unsigned decode_header(const data_buffer& buf)
    {
        if (buf.size() < HEADER_SIZE)
            return 0;
        unsigned msg_size = 0;
        for (unsigned i = 0; i < HEADER_SIZE-1; ++i)
            msg_size = msg_size * 256 + (static_cast<unsigned>(buf[i]) & 0xFF);
        return msg_size;
    }

	/*!
    *  \brief Unwrap the message.
    *
    */
    bool unpack(const data_buffer& buf)
    {
       return m_msg->ParseFromArray(&buf[HEADER_SIZE], buf.size() - HEADER_SIZE);
    }
private:

    void encode_header(data_buffer& buf, unsigned size) const
    {
        assert(buf.size() >= HEADER_SIZE);
        buf[0] = static_cast<boost::uint8_t>((size >> 24) & 0xFF);
        buf[1] = static_cast<boost::uint8_t>((size >> 16) & 0xFF);
        buf[2] = static_cast<boost::uint8_t>((size >> 8) & 0xFF);
        buf[3] = static_cast<boost::uint8_t>(size & 0xFF);
		buf[4] = messageType_;
    }

    MessagePointer m_msg;
};
}
#endif 

